﻿using NetCore.DataAccess.Utilties;
using NetCore.IBLL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace NetCore.DataAccess
{
    /// <summary>
    /// 针对 IEntityRepository 的具体实现
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class EntityRepository<T> : IEntityRepository<T> where T : class, IEntityBase, new()
    {
        readonly DbContext _EntitiesContext;

        public EntityRepository(DbContext context)
        {
            _EntitiesContext = context;
        }

        public virtual void Save()
        {
            _EntitiesContext.SaveChanges();
        }

        public virtual IQueryable<T> GetBoCollection() => _EntitiesContext.Set<T>();

        public virtual IQueryable<T> GetBoCollection(Expression<Func<T, bool>> predicate) => _EntitiesContext.Set<T>().Where(predicate);

        public virtual IQueryable<T> GetBoCollection(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _EntitiesContext.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }

        public virtual IQueryable<T> GetBoCollection(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _EntitiesContext.Set<T>().Where(predicate);
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;

        }

        public virtual T GetBo(Guid id) => GetBoCollection().FirstOrDefault(x => x.ID == id);

        public virtual T GetBo(Expression<Func<T, bool>> predicate) => _EntitiesContext.Set<T>().Where(predicate).FirstOrDefault();

        public virtual IList<T> GetBoPaginate<TKey>(int pageIndex, int pageSize, Expression<Func<T, TKey>> keySelector, out int count) => GetBoPaginate(pageIndex, pageSize, keySelector, null, out count);

        public virtual IList<T> GetBoPaginateDescend<TKey>(int pageIndex, int pageSize, Expression<Func<T, TKey>> keySelector, out int count) => GetBoPaginateDescend(pageIndex, pageSize, keySelector, null, out count);

        public virtual IList<T> GetBoPaginate<TKey>(
            int pageIndex,
            int pageSize,
            Expression<Func<T, TKey>> keySelector,
            Expression<Func<T, bool>> predicate,
            out int count,
            params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = GetBoCollection(includeProperties).OrderBy(keySelector);
            query = (predicate == null) ? query : query.Where(predicate);
            count = query.Count();
            return query.ToPaginatedList(pageIndex, pageSize);
        }

        public virtual IList<T> GetBoPaginateDescend<TKey>(int pageIndex, int pageSize, Expression<Func<T, TKey>> keySelector, Expression<Func<T, bool>> predicate, out int count, params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = GetBoCollection(includeProperties).OrderByDescending(keySelector);
            query = (predicate == null) ? query : query.Where(predicate);
            count = query.Count();
            return query.ToPaginatedList(pageIndex, pageSize);
        }

        public virtual void SaveBo(T entity)
        {
            if (IsNewBo(entity.ID))
            {
                _EntitiesContext.Set<T>().Add(entity);
            }
            else
            {
                var dbEntityEntry = _EntitiesContext.Entry<T>(entity);
                dbEntityEntry.State = EntityState.Modified;
            }
            _EntitiesContext.SaveChanges();

        }

        public virtual void DeleteBo(Guid id)
        {
            var bo = GetBo(id);
            DeleteBo(bo);
        }

        public virtual void DeleteBo(T entity)
        {
            var dbEntityEntry = _EntitiesContext.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Deleted;
            Save();
        }

        public virtual void DeleteBo(Expression<Func<T, bool>> predicate)
        {
            DbSet<T> dbSet = _EntitiesContext.Set<T>();
            var list = dbSet.Where(predicate);
            if (null != list)
            {
                foreach (var item in list)
                {
                    _EntitiesContext.Remove(item);
                }
            }
            Save();
        }

        public virtual IQueryable<T1> GetBoCollection<T1>(params Expression<Func<T1, object>>[] includeProperties) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T>();
            var query = dbSet as IQueryable<T1>;

            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }

        public virtual IQueryable<T1> GetBoCollection<T1>() where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T>();
            var query = dbSet as IQueryable<T1>;
            return query;
        }

        public virtual IQueryable<T1> GetBoCollection<T1>(Expression<Func<T1, bool>> predicate) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T>() as IQueryable<T1>;
            return dbSet.Where(predicate);
        }

        public virtual T1 GetBo<T1>(Guid id) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T1>();
            return dbSet.Find(id);

        }

        public virtual T1 GetBo<T1>(Expression<Func<T1, bool>> predicate) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T1>() as IQueryable<T1>;
            return dbSet.Where(predicate).FirstOrDefault();
        }

        public virtual void SaveBo<T1>(T1 entity) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T1>();
            var tempBo = GetBo<T1>(entity.ID);
            if (tempBo == null)
            {
                dbSet.Add(entity);
            }
            else
            {
                dbSet.Attach(tempBo);
                _EntitiesContext.Entry(tempBo).State = EntityState.Modified;
            }
            Save();
        }

        public virtual void DeleteBo<T1>(T1 entity) where T1 : class, IEntityBase, new()
        {

            var tempBo = Activator.CreateInstance(typeof(T1));
            tempBo = entity;
            var dbSet = _EntitiesContext.Set<T1>();

            dbSet.Attach(entity);
            dbSet.Remove(entity);
            Save();
        }

        public virtual void DeleteBo<T1>(Expression<Func<T, bool>> predicate) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T>();
            var list = dbSet.Where(predicate);
            if (null != list)
            {
                foreach (var item in list)
                {
                    _EntitiesContext.Remove(item);
                }
            }
        }


        public virtual bool IsNewBo(Guid id)
        {
            if (GetBo(id) == null)
                return true;
            else
                return false;
        }

        public virtual bool HasAny(Guid id)
        {
            if (GetBo(id) != null)
                return true;
            else
                return false;
        }

        public virtual void AddBo(T entity)
        {
            _EntitiesContext.Set<T>().Add(entity);
        }

        public virtual void AddBo<T1>(T1 entity) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T1>();
            dbSet.Add(entity);
        }

        public virtual void EditBo(T entity)
        {
            var dbEntityEntry = _EntitiesContext.Entry(entity);
            dbEntityEntry.State = EntityState.Modified;
        }

        public virtual void EditBo<T1>(T1 entity) where T1 : class, IEntityBase, new()
        {
            var dbSet = _EntitiesContext.Set<T1>();
            var tempBo = GetBo<T1>(entity.ID);
            dbSet.Attach(tempBo);
            _EntitiesContext.Entry(tempBo).State = EntityState.Modified;
        }

        public virtual void RemoveBo(T entity)
        {
            _EntitiesContext.Set<T>().Remove(entity);
        }

        public virtual void RemoveBo<T1>(T1 entity) where T1 : class, IEntityBase, new()
        {
            _EntitiesContext.Set<T1>().Remove(entity);
        }

       
    }
}
